package dto

import (
	"net/http"
	"log"
	"io/ioutil"
	"github.com/json-iterator/go"
	"fmt"
	"bytes"
	"strings"
	"time"
)

type DateSchema struct {
	// order: desc or acs
	Order string `json:"order,omitempty"`
}

func (it *DateSchema) SetOrder(order string) *DateSchema {
	it.Order = order
	return it
}

type FanSchema struct {
	// order: desc or acs
	Order string `json:"order,omitempty"`
}

func (it *FanSchema) FanSchema(order string) *FanSchema {
	it.Order = order
	return it
}

type SortSchema struct {
	Date interface{} `json:"date,omitempty"`
	Fan  interface{} `json:"fans,omitempty"`
}

func (it *SortSchema) SetDate(date DateSchema) *SortSchema {
	it.Date = date
	return it
}

func (it *SortSchema) SetFan(fan FanSchema) *SortSchema {
	it.Fan = fan
	return it
}

type MatchSchema struct {
	Title string `json:"title,omitempty"`
}

type QuerySchema struct {
	Match MatchSchema `json:"match,omitempty"`
}

type GetSearchSchema struct {
	Source []string    `json:"_source,omitempty"`
	Query  interface{} `json:"query,omitempty"`
	Size   int         `json:"size,omitempty"`
	From   int         `json:"from,omitempty"`
	Sort   interface{} `json:"sort,omitempty"`
}

func (it *GetSearchSchema) SetSource(source ...string) *GetSearchSchema {
	it.Source = source
	return it
}

func (it *GetSearchSchema) SetSize(size int) *GetSearchSchema {
	it.Size = size
	return it
}

func (it *GetSearchSchema) SetFrom(from int) *GetSearchSchema {
	it.From = from
	return it
}

func (it *GetSearchSchema) SetSort(sort SortSchema) *GetSearchSchema {
	it.Sort = sort
	return it
}

func (it *GetSearchSchema) SetQuery(query QuerySchema) *GetSearchSchema {
	it.Query = query
	return it
}

func (it *GetSearchSchema) Json(pretty bool) []byte {
	var (
		bytes []byte
		err   error
	)
	if pretty {
		bytes, err = jsoniter.MarshalIndent(it, "", "   ")
	} else {
		bytes, err = jsoniter.Marshal(it)
	}
	if err != nil {
		log.Println(err)
	}
	return bytes
}

type ElasticDto struct {
	client http.Client
	url    string
}

//初始化es客户端
func Init(url string) *ElasticDto {
	client := http.Client{}

	es := &ElasticDto{
		client: client,
		url:    url,
	}
	line := fmt.Sprintf("[ES] % -15s | % -30s |", time.Now().Format("2006/01/02 - 15:04:05"), "the es client has been init")
	log.Println(line)
	return es
}

// 测试es客户端
func (es *ElasticDto) Ping() {
	resp, err := es.client.Get(es.url)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Print(err)
	}
	fmt.Println(string(bytes))
}

func (es *ElasticDto) ProxyQuery(data []byte) []byte {
	resp, err := es.client.Post(fmt.Sprintf("%s/cookbooks/_search", es.url),
		"application/json",
		bytes.NewReader(data))
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()
	res, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}
	return res
}

func (es *ElasticDto) GetOneById(id string) []byte {
	resp, err := es.client.Get(fmt.Sprintf("%s/cookbooks/cookbook/%s", es.url, id))
	defer resp.Body.Close()
	if err != nil {
		log.Println(err)
		return []byte{}
	}
	bytes, err := ioutil.ReadAll(resp.Body)
	//fmt.Println("************")
	//fmt.Println(jsoniter.Get(bytes, "_id").ToString())
	if err != nil {
		log.Println(err)
		return []byte{}
	}
	return bytes
}

func (es *ElasticDto) GetLatestByPage(from int, size int) []byte {
	data := GetSearchSchema{}
	sort := SortSchema{
		Date: DateSchema{
			Order: "desc",
		},
	}
	data.SetFrom(from).SetSize(size).
		SetSource("title", "pic_url", "tags", "date", "order", "fans", "watch").
		SetSort(sort)
	resp, err := es.client.Post(fmt.Sprintf("%s/cookbooks/_search", es.url),
		"application/json",
		bytes.NewReader(data.Json(true)))
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()
	res, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}
	return res
}

func (es *ElasticDto) GetHotestByPage(from int, size int) []byte {
	data := GetSearchSchema{}
	sort := SortSchema{
		Fan: FanSchema{
			Order: "desc",
		},
	}
	data.SetFrom(from).SetSize(size).
		SetSource("title", "pic_url", "tags", "date", "order", "fans", "watch").
		SetSort(sort)

	resp, err := es.client.Post(fmt.Sprintf("%s/cookbooks/_search", es.url),
		"application/json",
		bytes.NewReader(data.Json(true)))
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()
	res, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}
	return res
}

func (es *ElasticDto) GetCoursels() []byte {
	data := GetSearchSchema{}
	sort := SortSchema{
		Fan: FanSchema{
			Order: "desc",
		},
	}
	data.SetFrom(0).SetSize(5).
		SetSource("title", "pic_url").
		SetSort(sort)

	resp, err := es.client.Post(fmt.Sprintf("%s/cookbooks/_search", es.url),
		"application/json",
		bytes.NewReader(data.Json(true)))
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()
	res, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}
	return res
}

/**
	cateType -> type TODO
 */
func (es *ElasticDto) GetByCategory(cateType string, from int, size int, sortType string) []byte {
	data := GetSearchSchema{}
	var sort SortSchema
	if sortType == "date" {
		sort = SortSchema{
			Fan: FanSchema{
				Order: "desc",
			},
		}
	} else {
		sort = SortSchema{
			Date: DateSchema{
				Order: "desc",
			},
		}
	}
	data.SetFrom(from).SetSize(size).
		SetSource("title", "pic_url", "tags", "date", "order", "fans", "watch").
		SetSort(sort).
		SetQuery(QuerySchema{
		Match: MatchSchema{
			Title: cateType,
		},
	})

	resp, err := es.client.Post(fmt.Sprintf("%s/cookbooks/_search", es.url),
		"application/json",
		bytes.NewReader(data.Json(true)))
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()
	res, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}
	return res
}

func (es *ElasticDto) SearchByKeyword(keyword string) []byte {
	var query = `
		{
			"_source" : ["title", "pic_url", "date"],
			"suggest" : {
				"cook_suggests":{
					"text" : "%s",
					"completion": {
					  "field": "suggest",
					  "fuzzy": {
						"fuzziness": 1
					  },
					  "size": 5
					}
				}
			}
		}
	`

	fmt.Println(fmt.Sprintf(query, keyword))

	resp, err := es.client.Post(fmt.Sprintf("%s/cookbooks/_search", es.url),
		"application/json",
		strings.NewReader(fmt.Sprintf(query, keyword)))
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()
	res, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}
	return res
}

func (es *ElasticDto) SpecialSearch(query string) []byte {
	var body = `
		{
			"_source" : ["title", "pic_url", "date"],
			"size": 10,
			"query" : {
				"query_string" : {
					"fields": ["title", "tags"],
					"query":"%s",
					"analyzer":"ik_max_word"
				}
			}
		}
	`
	resp, err := es.client.Post(fmt.Sprintf("%s/cookbooks/_search", es.url),
		"application/json",
		strings.NewReader(fmt.Sprintf(body, query)))
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()
	res, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}
	return res
}

func (es *ElasticDto) WordAnalyze(text string) []byte {
	var body = `
		{
			"analyzer": "ik_smart",
			 "text": "%s"
		}
	`
	fmt.Println(fmt.Sprintf(body, text))

	resp, err := es.client.Post(fmt.Sprintf("%s/_analyze", es.url),
		"application/json",
		strings.NewReader(fmt.Sprintf(body, text)))
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()
	res, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}
	return res
}

//Reader 读者 负责读数据
//Writer 写者 负责写数据
